import os
import chardet

'''
遍历目录里的所有文件，过滤掉不统计的文件，返回完整的文件路径列表
'moc_'前缀的文件不统计（Qt自动生成的）
只统计后缀为'.h', '.cpp', '.c', '.hpp', '.cc'的文件
'''
def valid_file_list(dir):
  def invalid_prefix(file):
    for prefix in ['moc_']:
      if file.startswith(prefix):
        return True
    return False

  def valid_suffix(file):
    for suffix in ['.h', '.cpp', '.c', '.hpp', '.cc']:
      if file.endswith(suffix):
        return True
    return False

  ls = []
  for subdir, dirs, files in os.walk(dir):
    for file in files:
      if invalid_prefix(file):
        continue
      if not valid_suffix(file):
        continue
      filepath = subdir + os.sep + file  
      ls.append(filepath)
  return ls


'''
读取代码文件，默认使用utf-8编码打开。
如果打开失败，使用chardet获取文件编码，再次使用新的编码打开，
决定文件是什么编码是相对而言是比较耗时的。
'''
def read_text(path):
  encoding = 'utf-8'
  try:
    for _ in range(2):
      try:
        with open(path, encoding=encoding) as f:
          return f.read()
      except UnicodeError as e:
        with open(path, 'rb') as f:
          encoding = chardet.detect(f.read())['encoding']
          print('detect encoding:', encoding)
  except Exception as e:
    print(e)
  return ''


'''
统计文本中的代码行数
排除C++注释，空行
'''
def count_code_line_from_text(text):
  ls = str.split(text, '\n')
  ignore = False
  count = 0
  for v in ls:
    v = v.strip()
    if len(v) == 0:
      continue
    if v.startswith('//'):
      continue
    if v.startswith('/*'):
      ignore = True
      # 注意这里不能用continue，否则在同一行会出问题
    if ignore:
      if v.endswith('*/'):
        ignore = False
      continue
    count += 1
  return count


'''
统计目录下所有有效代码文件行数，
打印文件总数和代码总行数。
'''
def count_code_line_from_dir(dir):
  files = valid_file_list(dir)
  total = 0
  for file in files:
    text = read_text(file)
    count = count_code_line_from_text(text)
    print(file, count)
    total += count
  print('total files:%d, lines:%d' % (len(files), total))


if __name__ == "__main__":
  count_code_line_from_dir('E:\\project\\test')

  